/**
 * 
 */
package org.ranch.web.controllers;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.ranch.api.RanchConstants;
import org.ranch.api.security.PermissionConstants;
import org.ranch.api.service.ConceptService;
import org.ranch.api.utils.RanchStringUtil;
import org.ranch.model.Concept;
import org.ranch.model.ConceptCategory;
import org.ranch.model.exception.RanchValidationException;
import org.ranch.web.WebConstants;
import org.ranch.web.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

/**
 *
 */
@Controller
@RequestMapping("concept")
public class ConceptController {

    @Autowired
    private ConceptService conceptService;

    /**
     * handler method to handle the view for concepts by category
     * 
     * @param conceptCategoryId
     * @param pageNo
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.POST, params = { "action=view",
			"item=concepts" })
    public ModelAndView viewConceptsByCategoryHandler(
			@RequestParam(value = "cid", required = false) String conceptCategoryId,
			@RequestParam(value = "pageNo", required = false) Integer pageNo,
			ModelMap model) {
	return viewConceptsHandler(conceptCategoryId, pageNo, model);
    }

    /**
     * handler method to handle the view for concepts
     * 
     * @param pageNo
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.GET, params = { "action=view",
			"item=concepts" })
    public ModelAndView viewConceptsHandler(
			@RequestParam(value = "cid", required = false) String conceptCategoryId,
			@RequestParam(value = "pageNo", required = false) Integer pageNo,
			ModelMap model) {

	if (pageNo == null || (pageNo != null && pageNo <= 0)) {
	    pageNo = 1;
	}

	List<ConceptCategory> categories = conceptService
				.getAllConceptCategories();
	categories.add(0, new ConceptCategory());
	model.put("conceptcategories", categories);
	List<Concept> concepts = new ArrayList<Concept>();

	if (conceptCategoryId == "")
	    conceptCategoryId = null;
	if (conceptCategoryId != null) {
	    ConceptCategory conceptCategory = conceptService
					.getConceptCategoryById(conceptCategoryId);
	    model.put("conceptCategory", conceptCategory);
	    concepts = conceptService.getConceptsByCategory(conceptCategory,
					pageNo);
	    model.put("concepts", concepts);

	    WebUtils.prepareNavigation("Concept", conceptService
					.getNumberOfConceptsByCategory(conceptCategory), concepts
					.size(), pageNo,
					"concept?action=view&item=concepts&cid="
							+ conceptCategory.getId(), model);
	} else {
	    model.put("conceptCategory", new ConceptCategory());
	    concepts = conceptService.getConceptByPage(pageNo);
	    model.put("concepts", concepts);

	    WebUtils.prepareNavigation("Concept",
					conceptService.getNumberOfConcepts(), concepts.size(),
					pageNo, "concept?action=view&item=concepts", model);
	}

	return new ModelAndView("conceptView", model);
    }

    /**
     * handler method to handle the request for adding a concept in the system
     * 
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.GET, params = { "action=add",
			"item=concept" })
    public ModelAndView viewAddConceptHandler(ModelMap model) {
	prepareConceptFormModel(model);
	model.put("concept", new Concept());

	return new ModelAndView("addConcept", model);
    }

    private void prepareConceptFormModel(ModelMap model) {
	List<ConceptCategory> conceptCategories = conceptService
				.getAllConceptCategories();
	model.put("conceptcategories", conceptCategories);
    }

    /**
     * handler method to handle the request for editing a concept in the system
     * 
     * @param conceptId
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.GET, params = { "action=edit",
			"item=concept" })
    public ModelAndView viewEditConceptHandler(
			@RequestParam("id") String conceptId, ModelMap model) {
	Concept concept = conceptService.getConceptById(conceptId);
	if (concept != null) {
	    prepareConceptFormModel(model);
	    model.put("concept", concept);

	    return new ModelAndView("editConcept", model);
	} else {
	    model.put(
		    WebConstants.MODEL_ATTRIBUTE_ERROR_MESSAGE,
					"the id of the concept supplied doesn't match a concept in the system");
	    return viewConceptsHandler(null, null, model);
	}
    }

    /**
     * handler method to have the request for saving a concept
     * 
     * @param concept
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.POST, params = { "action=save",
			"item=concept" })
    public ModelAndView saveConceptHandler(
			@ModelAttribute("concept") Concept concept, ModelMap model) {
	if (concept != null) {
	    try {

		if (StringUtils.isBlank(concept.getId()))
		    concept.setId(null);

		concept.setName(RanchStringUtil.capitalizeFirstLetters(concept
						.getName()));
		conceptService.saveConcept(concept);
		model.put(WebConstants.MODEL_ATTRIBUTE_SYSTEM_MESSAGE,
						"concept saved successfully");
	    } catch (RanchValidationException ex) {
		model.put(WebConstants.MODEL_ATTRIBUTE_ERROR_MESSAGE,
						ex.getMessage());
		prepareConceptFormModel(model);
		model.put("concept", concept);

		return new ModelAndView("editConcept", model);
	    }
	}

	// prepare form here so that in case of an exception while
	// saving the concept, the preparation is not skipped
	//prepareConceptFormModel(model);
	//model.put("concept", concept);
	return viewConceptsHandler("", 0, model);
    }

    /**
     * handler method to handle a request for deleting a concept
     * 
     * @param conceptIds
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.POST, params = { "action=delete",
			"item=concept" })
    public void deleteConceptHandler(
			@RequestParam("selectedConcepts") List<String> conceptIds,
			HttpServletResponse response) {

	try {
	    if (conceptIds != null) {
		conceptService.deleteConcepts(conceptIds);
		response.setStatus(HttpServletResponse.SC_OK);
		response.getWriter().write("concept(s) deleted successfully");
	    } else {
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		response.getWriter().write(
						"no concept(s) supplied for deleting");
	    }
	} catch (IOException e) {
	}
    }

    /**
     * handler method to handle the request for searching a concept
     * 
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.POST, params = { "action=search","item=concept" })
    public ModelAndView searchConcept(
			@RequestParam(value = "pageNo", required = false) Integer pageNo,
			@RequestParam(WebConstants.SEARCH_QUERY_REQUEST_PARAMETER_NAME) String query,
			ModelMap model) {

	model.put("query", query);

	if (pageNo == null || (pageNo != null && pageNo <= 0)) {
	    pageNo = 1;
	}

	List<ConceptCategory> categories = conceptService
				.getAllConceptCategories();
	categories.add(0, new ConceptCategory());
	model.put("conceptcategories", categories);
	model.put("conceptCategory", new ConceptCategory());

	List<Concept> concepts = conceptService.searchConceptByName(query,
				pageNo);

	if (concepts != null && concepts.size() == 0) {
	    model.put(WebConstants.MODEL_ATTRIBUTE_SYSTEM_MESSAGE,
					"search for query >> \"" + query
							+ "\" completed with no results");
	}

	if (concepts != null && concepts.size() > 0) {

	    model.put("concepts", concepts);

	    model.put(
					WebConstants.MODEL_ATTRIBUTE_SYSTEM_MESSAGE,
					"search for query >> \"" + query
							+ "\" completed successfully with "
							+ concepts.size() + " results");
	    WebUtils.prepareNavigation("Concept", conceptService
					.getTotalNumberOfConceptsInSearch(query, pageNo), concepts
					.size(), pageNo, "controlpanel?action=search&item=concept&"
					+ WebConstants.SEARCH_QUERY_REQUEST_PARAMETER_NAME + "="
					+ query, model);
	}

	return new ModelAndView("conceptView", model);
    }

    /**
     * handler method to handle the request for searching a concept
     * 
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.GET, params = { "action=search",
			"item=concept" })
    public ModelAndView searchConceptNavigation(
			@RequestParam(value = "pageNo", required = false) Integer pageNo,
			@RequestParam(value = WebConstants.SEARCH_QUERY_REQUEST_PARAMETER_NAME,
				required = false) String query,
			ModelMap model) {
	return searchConcept(pageNo, query, model);
    }

    /**
     * handler method to handle the view for concept categories
     * 
     * @param pageNo
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.GET, params = { "action=view",
			"item=conceptcategories" })
    public ModelAndView viewConceptCategoryHandler(
			@RequestParam(value = "pageNo", required = false) Integer pageNo,
			ModelMap model) {

	if (pageNo == null || (pageNo != null && pageNo <= 0)) {
	    pageNo = 1;
	}

	List<ConceptCategory> conceptCategories = conceptService
				.getConceptCategoryByPage(pageNo);
	model.put("conceptcategories", conceptCategories);

	int numberOfConcepts = conceptService.getNumberOfConceptCategories();

	prepareItemPaging(pageNo, model, numberOfConcepts);
	return new ModelAndView("conceptcategoryView", model);
    }

    /**
     * prepares the paging and adds paging attributes to the given model
     * 
     * @param pageNo
     *            current page number
     * @param model
     *            model in which to add the paging attributes
     * @param numberOfItems
     *            total number of items to page
     */
    private void prepareItemPaging(Integer pageNo, ModelMap model,
			int numberOfItems) {
	double numberOfPages = numberOfItems
				/ (double) RanchConstants.MAX_NUM_PAGE_RECORD;
	int numPages = (int) Math.rint(numberOfPages);

	/*
	 * if the number of pages after the round off is less than the computed
	 * number of pages, then we increment the rounded off number by 1 so
	 * that the records that are in the fraction of the page are shown.
	 */
	if (numPages < numberOfPages) {
	    numPages += 1;
	}

	model.put("numberofItems", numberOfItems);
	model.put("numberOfPages", numPages);
	model.put("currentPage", pageNo);
    }

    /**
     * handler method to handle the request for adding a concept category in the
     * system
     * 
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.GET, params = { "action=add",
			"item=conceptcategory" })
    public ModelAndView viewAddConceptCategoryHandler(ModelMap model) {
	model.put("conceptcategory", new ConceptCategory());
	return new ModelAndView("addConceptCategory", model);
    }

    /**
     * handler method to handle the request for editing a concept category in
     * the system
     * 
     * @param conceptCategoryId
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.GET, params = { "action=edit",
			"item=conceptcategory" })
    public ModelAndView viewEditConceptCategoryHandler(
			@RequestParam("id") String conceptCategoryId, ModelMap model) {
	ConceptCategory conceptCategory = conceptService
				.getConceptCategoryById(conceptCategoryId);
	if (conceptCategory != null) {
	    model.put("conceptcategory", conceptCategory);

	    return new ModelAndView("editConceptCategory", model);
	} else {
	    model.put(
					WebConstants.MODEL_ATTRIBUTE_ERROR_MESSAGE,
					"the id of the concept category supplied doesn't match a concept category in the system");
	    return viewConceptsHandler(null, 1, model);
	}
    }

    /**
     * handler method to have the request for saving a concept category
     * 
     * @param conceptCategory
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.POST, params = { "action=save",
			"item=conceptcategory" })
    public ModelAndView saveConceptCategoryHandler(
			@ModelAttribute("conceptcategory") ConceptCategory conceptCategory,
			ModelMap model) {
	if (conceptCategory != null) {
	    try {
		conceptService.saveConceptCategory(conceptCategory);
		model.put("conceptcategory", conceptCategory);
		model.put(WebConstants.MODEL_ATTRIBUTE_SYSTEM_MESSAGE,
						"concept category saved successfully");
	    } catch (RanchValidationException ex) {
		model.put(WebConstants.MODEL_ATTRIBUTE_ERROR_MESSAGE,
						ex.getMessage());
	    }
	}

	return new ModelAndView("editConceptCategory", model);
    }

    /**
     * handler method to handle a request for deleting a concept category
     * 
     * @param conceptCategoryId
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.GET, params = { "action=delete",
			"item=conceptcategory" })
    public ModelAndView deleteConceptCategoryHandler(
			@RequestParam("id") String conceptCategoryId, ModelMap model) {
	ConceptCategory conceptCategory = conceptService
				.getConceptCategoryById(conceptCategoryId);
	if (conceptCategory != null) {
	    conceptService.deleteConceptCategory(conceptCategory);
	    model.put(WebConstants.MODEL_ATTRIBUTE_SYSTEM_MESSAGE,
					"concept category >> " + conceptCategory.getName()
							+ " deleted successfully");
	} else {
	    model.put(
					WebConstants.MODEL_ATTRIBUTE_ERROR_MESSAGE,
					"the concept category id supplied does not belong to a valid concept in the system");
	}
	return viewConceptCategoryHandler(null, model);
    }

    /**
     * handler method to handle the request for searching a concept category
     * 
     * @param request
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.POST, params = { "action=search",
			"item=conceptcategory" })
    public ModelAndView searchConceptCategory(
			@RequestParam(WebConstants.SEARCH_QUERY_REQUEST_PARAMETER_NAME) String query,
			ModelMap model) {
	List<ConceptCategory> conceptCategories = conceptService
				.searchConceptCategoriesByName(query);
	model.put("conceptcategories", conceptCategories);

	if (conceptCategories != null && conceptCategories.size() == 0) {
	    model.put(WebConstants.MODEL_ATTRIBUTE_SYSTEM_MESSAGE,
					"search for query >> \"" + query
							+ "\" completed with no results");
	}

	return new ModelAndView("conceptcategoryView", model);
    }
}
